package com.appspot.yusupova;

import java.io.IOException;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>This is the base class for all servlets processing requests from the remote administrator.
 * 
 * <p>It checks a "password" HTTP POST parameters and 
 *  
 * @author Rouslan Shchoutchinov
 */
@SuppressWarnings("serial")
abstract class AdminHttpServlet extends HttpServlet {

    // First part of HASH. The second part must be transmitted by the administrator.
    private static final byte[] ADMIN_PART_ONE = new byte[] {
        (byte) 0x9F, (byte) 0x80, (byte) 0xBC, (byte) 0x28, 
        (byte) 0x65, (byte) 0x5C, (byte) 0xCF, (byte) 0x84, 
        (byte) 0xF9, (byte) 0x5F, (byte) 0xAF, (byte) 0xFB, 
        (byte) 0xC5, (byte) 0xC0, (byte) 0x23, (byte) 0x2D, 
        (byte) 0x79, (byte) 0x78, (byte) 0x7E, (byte) 0x3E, 
        (byte) 0xE7, (byte) 0xFB, (byte) 0x74, (byte) 0x98, 
        (byte) 0x0D, (byte) 0xF4, (byte) 0x4A, (byte) 0x04, 
        (byte) 0x0A, (byte) 0x7B, (byte) 0x7A, (byte) 0xA8, 
    };
    
    // CONTROL_SERVER_HASH = MD5(ADMIN_PART_ONE | getHex(<right administrator password>))
    private static final String CONTROL_SERVER_HASH = "2d59cf2527724fcf61f8a4e43ed7c5ad";
    
    // administrator password
    private static final String REQ_PARAM_PASSWORD = "password";

    /**
     * Checks if the user have sent the right password in a "password" HTTP POST parameters
     * and continues request processing in doAuthorizedPost() method if the password is OK.     
     * 
     * @param req  client's request
     * @param resp server's response
     * 
     * @throws IOException if it can't perform some I/O operations in a derived class.
     */
    public final void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("text/plain");
        PrintWriter writer = resp.getWriter();
        try {
            String password = req.getParameter(REQ_PARAM_PASSWORD);
            if (password != null) {
                try {
                    if (isAdminPassword(password)) {
                        doAuthorizedPost(req, resp, writer);
                    } else {
                        writer.println("Wrong password");
                    }
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                    writer.println("Cannot check password:" + e.toString() + "(" + e.getMessage() + ")");
                }
            } else {
                writer.println("Empty password!");
            }
        } finally {
            writer.close();
        }
    }

    /**
     * Process the request after checking administrator password. 
     * 
     * @param req    client request
     * @param resp   server response
     * @param writer a writer to print formatted response
     * 
     * @throws IOException if it can't perform some I/O operations in a derived class.
     */
    protected abstract void doAuthorizedPost(HttpServletRequest req, HttpServletResponse resp, 
            PrintWriter writer) throws IOException;
    
    // checks if the password is administrator password 
    private static boolean isAdminPassword(String password) throws NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        digest.update(ADMIN_PART_ONE);
        digest.update(password.getBytes());
        byte[] hash = digest.digest();
        String hashToCheck = getHex(hash);
        return CONTROL_SERVER_HASH.equals(hashToCheck);
    }
    
    // converts a byte array to a string 
    private static String getHex(byte[] bytes) {
        StringBuilder buffer = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() < 2) {
                buffer.append('0');
            }
            buffer.append(hex);
        }
        return buffer.toString();
    }

}
